package org.zhq.client;

import org.zhq.client.bean.ServerInfo;
import org.zhq.constants.UDPConstants;
import org.zhq.util.ByteUtils;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * @author zhengquan
 * @date 2018/12/23
 */
public class UDPSearcher {

    public static final int LISTEN_PORT = UDPConstants.PORT_CLIENT_RESPONSE;

    public static ServerInfo searchServer(int timeout) {
        System.out.println("UDPSearcher Started");

        //成功收到回送的栅栏
        CountDownLatch receiveLatch = new CountDownLatch(1);
        Listener listener = null;
        try {
            listener = listen(receiveLatch);
            sendBroadcast();
            receiveLatch.await(timeout, TimeUnit.MILLISECONDS);
        } catch (Exception ignore) {
        }

        System.out.println("UDPSearcher Finished");
        if (listener == null) {
            return null;
        }
        List<ServerInfo> servers = listener.getServerAndClose();
        if (servers.size() > 0) {
            return servers.get(0);
        }
        return null;
    }


    private static Listener listen(CountDownLatch receiveLatch) throws InterruptedException {
        System.out.println("UDPSearcher start listen");
        CountDownLatch startCountLatch = new CountDownLatch(1);
        Listener listener = new Listener(LISTEN_PORT, startCountLatch, receiveLatch);
        listener.start();
        startCountLatch.await();
        return listener;
    }

    private static void sendBroadcast() throws IOException {
        System.out.println("UDPSearcher sendBroadcast started");

        //作为搜索方,让系统自动分配端口
        DatagramSocket ds = new DatagramSocket();
        //构建一份请求数据
        ByteBuffer byteBuffer = ByteBuffer.allocate(128);
        //头部
        byteBuffer.put(UDPConstants.HEADER);
        //cmd命名
        byteBuffer.putShort((short)1);
        //回送端口信息
        byteBuffer.putInt(LISTEN_PORT);
        //直接构建packet
        DatagramPacket requestPacket = new DatagramPacket(byteBuffer.array(),byteBuffer.position()+1);
        //广播地址
        requestPacket.setAddress(InetAddress.getByName("255.255.255.255"));
        //设置服务端口
        requestPacket.setPort(UDPConstants.PORT_SERVER);

        ds.send(requestPacket);
        ds.close();

        System.out.println("UDPSearcher sendBroadcast finish");
    }


    private static class Listener extends Thread {
        private final int listenPort;
        private final CountDownLatch startCountLatch;
        private final CountDownLatch receiveCountLatch;

        private final List<ServerInfo> serverInfoList = new ArrayList<>();

        private final byte[] buffer = new byte[128];

        private final int minLen = UDPConstants.HEADER.length + 2 + 4;

        private boolean done = false;

        private DatagramSocket ds = null;

        public Listener(int listenPort, CountDownLatch startCountLatch, CountDownLatch receiveCountLatch) {
            this.listenPort = listenPort;
            this.startCountLatch = startCountLatch;
            this.receiveCountLatch = receiveCountLatch;
        }


        @Override
        public void run() {
            super.run();
            //通知线程已启动
            startCountLatch.countDown();

            try {
                //监听回送端口
                ds = new DatagramSocket(listenPort);
                //构建接收实体
                DatagramPacket receivePacket = new DatagramPacket(buffer, buffer.length);
                while (!done) {
                    //接收
                    ds.receive(receivePacket);

                    String ip = receivePacket.getAddress().getHostAddress();
                    int port = receivePacket.getPort();
                    int dataLen = receivePacket.getLength();
                    byte[] data = receivePacket.getData();
                    boolean isValid = dataLen >= minLen && ByteUtils.startWith(data, UDPConstants.HEADER);
                    System.out.println("UDPSearcher receive from ip:" + ip + " port:" + port + " isValid:" + isValid);

                    if (!isValid) {
                        //跳过这条无效信息
                        continue;
                    }
                    ByteBuffer byteBuffer = ByteBuffer.wrap(buffer, UDPConstants.HEADER.length, dataLen);
                    final short cmd = byteBuffer.getShort();
                    final int serverPort = byteBuffer.getInt();
                    if (cmd != 2 || serverPort <= 0) {
                        System.out.println("UDPSearcher receive cmd:" + cmd + "serverPort:" + serverPort + " fail !!!!!");
                        continue;
                    }
                    String sn = new String(buffer, minLen, dataLen - minLen);
                    ServerInfo serverInfo = new ServerInfo(sn, serverPort, ip);
                    serverInfoList.add(serverInfo);

                    receiveCountLatch.countDown();
                }

            } catch (Exception ignore) {
            } finally {
                close();
            }
            System.out.println("UDPSearcher finish listen");
        }

        private void close() {
            if (ds != null) {
                ds.close();
            }
            ds = null;

        }

        public List<ServerInfo> getServerAndClose() {
            done = true;
            close();
            return serverInfoList;
        }

    }
}
